package org.jfonia.connect5.collections;

import org.jfonia.connect5.basics.MutableValueNode;
import org.jfonia.connect5.basics.Observer;
import org.jfonia.connect5.basics.ValueNode;

/**
 * @author wijnand.schepens@hogent.be
 */
public abstract class MembershipSystem<T> implements Observer
{
	SetNode<T> members;

	// aux:
	ValueNode<T> additionNode;
	ValueNode<T> removalNode;

	public MembershipSystem(SetNode<T> members_)
	{
		this.members = members_;

		additionNode = members.getAdditionNode();
		additionNode.addObserver(this);
		removalNode = members.getRemovalNode();
		removalNode.addObserver(this);

		for (T cand : members)
			addCandidate(cand);
	}

	public void addCandidate(T cand)
	{
		MutableValueNode<Boolean> mn = selectMembershipNode(cand);
		Helper h = new Helper(cand, this);
		mn.addObserver(h);
		h.onNotify(mn); // initialization
	}

	public void onNotify(Object source)
	{
		if (source == additionNode)
			selectMembershipNode(additionNode.getValue()).setValue(true, this);
		else if (source == removalNode)
			selectMembershipNode(removalNode.getValue()).setValue(false, this);
		else // if (source instanceof Helper)
		{
			Helper h = (Helper)source;
			T parent = h.getParent();
			if ( selectMembershipNode(parent).getValue().booleanValue() )
				members.add(parent, this);
			else
				members.remove(parent, this);
		}
	}
	
	public SetNode<T> getMembers()
	{
		return members;
	}

	// TODO: removeCandidate

	public abstract MutableValueNode<Boolean> selectMembershipNode(T candidate);


	class Helper implements Observer {
		T parent;
		Observer target;

		public Helper(T parent, Observer target)
		{
			this.parent = parent;
			this.target = target;
		}

		public void onNotify(Object source)
		{
			target.onNotify(this); // not source!
		}

		public T getParent()
		{
			return parent;
		}
	}
}
